﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Transform;
using NHibernate.Criterion;
using ScheduleMaker.ConfigManager;

namespace ScheduleMaker.DB
{
    public abstract class AbstractDAO<T>
    {
        protected static Configuration config;
        protected static ISessionFactory sessionFactory;
        internal static ISession session;
        protected ITransaction transaction;
        private Boolean transactionBegan;

        protected void configure()
        {
            if (!ConfigManager.ConfigManager.isConfigLoaded())
            {
                //rzuc wyjatek czy cos
            }
            Config configuration = ConfigManager.ConfigManager.getConfiguration();
            String connectionString = createConnectionString(configuration);
            config = new Configuration()
                .Proxy(p => p.ProxyFactoryFactory<NHibernate.Bytecode.DefaultProxyFactoryFactory>())
                .DataBaseIntegration(d =>
                    {
                        d.ConnectionString = connectionString;
                        d.Dialect<NHibernate.Dialect.MySQLDialect>();
                    });
            //config = new Configuration();
            //TODO: znalezc lepszy sposob na dodanie mappingow, ten jest brzydki
            config.AddAssembly(typeof(ScheduleMaker.DB.Domain.LevelSchool).Assembly);
            sessionFactory = config.BuildSessionFactory();
        }

        protected string createConnectionString(Config configuration)
        {
            char[] chars = {'/'};
            string[] serverDb = configuration.Addr.Split(chars);
            string login = configuration.Login;
            string pass = configuration.Password;
            return String.Format("Server={0};Database={1};Uid={2};Pwd={3};", 
                serverDb[0],
                serverDb[2],
                login,
                pass);
        }

        internal void createSession()
        {
            if (sessionFactory == null)
            {
                configure();
            }
            if (session == null)
            {
                session = sessionFactory.OpenSession();
            }
        }

        internal void createTransaction()
        {
            if (session == null)
            {
                createSession();
            }
            transaction = session.BeginTransaction();
            transactionBegan = true;
        }

        internal void commitTransaction()
        {
            if (transactionBegan)
            {
                transaction.Commit();
            }
        }

        public void openSession()
        {
            if (session == null)
            {
                createSession();
            }
        }

        public void closeSession()
        {
            if (session != null)
            {
                session.Close();
                session = null;
            }
        }

        public void save(T entity)
        {
            createTransaction();
            session.Save(entity);
            commitTransaction();
        }

        public void delete(T entity)
        {
            createTransaction();
            session.Delete(entity);
            commitTransaction();
        }

        public void update(T entity)
        {
            createTransaction();
            session.Update(entity);
            commitTransaction();
        }

        public void saveOrUpdate(T entity)
        {
            createTransaction();
            session.SaveOrUpdate(entity);
            commitTransaction();
        }

        public T getById(int id)
        {
            T entity;
            createSession();
            entity = session.Get<T>(id);
            return entity;
        }

        public IList<T> getAll()
        {
            createSession();
            ICriteria criteria = session.CreateCriteria(typeof(T)).SetResultTransformer(new DistinctRootEntityResultTransformer()).AddOrder(Order.Asc("id"));
            IList<T> records = criteria.List<T>();
            return records;
        }
    }
}
